package com.baofu.paycore.biz.paycore.handler.impl;

import com.baofu.clear.service.facade.enums.EventTypeEnum;
import com.baofu.paycore.biz.external.account.AccountBaseService;
import com.baofu.paycore.biz.external.account.converter.AccountResultConverter;
import com.baofu.paycore.biz.external.account.models.AccountFundResBO;
import com.baofu.paycore.biz.external.clear.EventService;
import com.baofu.paycore.biz.external.clear.model.EventRuleResBO;
import com.baofu.paycore.biz.model.BackPayBO;
import com.baofu.paycore.biz.paycore.converter.BO2BOConverter;
import com.baofu.paycore.common.enums.FundTypeEnum;
import com.baofu.paycore.manager.model.RechargeBO;
import com.baofu.paycore.manager.model.RechargeFundBO;
import com.baofu.paycore.manager.model.ResponseBO;
import com.baofu.paycore.manager.storer.RechargeOrderRepository;
import com.baofu.paycore.service.facade.enums.PayStatusEnums;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Objects;

/**
 * 线下打款充值订单处理handler
 * <p>
 * </p>
 * User: kwl Date: 2017/3/3 ProjectName: paycore Version: 5.0.0
 */
@Slf4j
@Service
public class OfflineRechargeHandler extends RechargeHandler {

    /** 会计事件服务 */
    @Autowired
    private EventService eventService;

    /** 账务充值 */
    @Autowired
    protected AccountBaseService accountRechargeService;

    /** 持久化服务 */
    @Autowired
    private RechargeOrderRepository rechargeOrderRepository;

    /**
     * 线下充值流程
     *
     * <p>
     * 线下打款充值，由于商户已经线下打过款，钱已经到了公司银行账户，所以，无需再次调用银行的充值，具体如下：
     * 1、根据商户打款的凭证编号（白编号由前端传递），调用银行的打款验证接口，验证商户打款信息。
     * 2、根据查询打款信息，验证商户线下打款录入订单信息（如：打款金额等）。
     * 3、验证成功，确保打款订单无问题后，调用账户系统进行打款转账。
     *
     * 注：第一阶段，暂不走步骤1，即暂不调用银行去验证商户的打款信息，先直接调用账务充值接口，进行余额
     * 打款充值。
     *
     * @param rechargeBO    充值订单
     * @return              充值结果
     */
    public BackPayBO offlineRechargeProcess(RechargeBO rechargeBO){

        //会计事件
        eventProcess(rechargeBO);

        //账务充值
        rechargeProcess(rechargeBO);

        //状态推进
        orderStatusPush(rechargeBO);

        //返回结果
        return BO2BOConverter.rechargeBO2BackPayBO(rechargeBO);
    }

    /**
     * 会计事件处理流程
     *
     * <p>
     * 按现有流程，若充值单中包含手续费时，手续费的会计事件已经在订单信息构建时确认。
     * 此处会计事件流程只针对账务充值资金单来确认。 同时，查询会计事件时，需要根据
     * 产品细类、产品渠道2种信息去确认。
     * </p>
     *
     * @param rechargeBO    充值订单
     */
    private void eventProcess(RechargeBO rechargeBO){

        //账务充值资金单
        RechargeFundBO rechargeFundBO = rechargeBO.getRechargeFund(FundTypeEnum.RECHARGE.getCode());

        //查询会计事件号
        EventRuleResBO eventRuleResBO = eventService.queryByEventInfo(EventTypeEnum.PRODUCT_CHANNEL,
                rechargeBO.getSubBizType(), rechargeBO.getProductChannel());

        BO2BOConverter.eventResBO2RechargeBO(eventRuleResBO, rechargeBO, rechargeFundBO);
    }

    /**
     * 线下打款充值流程
     *
     * @param rechargeBO    充值订单
     */
    private void rechargeProcess(RechargeBO rechargeBO){

        //状态判断
        if(!PayStatusEnums.SUCCESS.getCode().equals(rechargeBO.getStatus())) {
           return;
        }

        //充值资金单
        RechargeFundBO rechargeFundBO = rechargeBO.getRechargeFund(FundTypeEnum.RECHARGE.getCode());

        if(!rechargeProcess(rechargeBO, rechargeFundBO)){
            rechargeBO.setStatus(rechargeFundBO.getStatus());
            return;
        }

        //收费资金单
        RechargeFundBO feeFundBO = rechargeBO.getRechargeFund(FundTypeEnum.FEE.getCode());

        if(Objects.isNull(feeFundBO)) {
            return;
        }
        if(feeProcess(rechargeBO, feeFundBO)){
            rechargeBO.setStatus(rechargeFundBO.getStatus());
        }
        rechargeBO.setStatus(PayStatusEnums.SUCCESS.getCode());
    }

    /**
     * 手续费转账处理流程
     *
     * @param rechargeBO        充值单
     * @param rechargeFundBO    手续费资金单
     * @return                  流程成功与否
     */
    private boolean rechargeProcess(RechargeBO rechargeBO, RechargeFundBO rechargeFundBO){

        //调用账务转账接口
        AccountFundResBO accountFundResBO = accountRechargeService.fundHandle(rechargeBO, rechargeFundBO);

        AccountResultConverter.result2RechargeBO(accountFundResBO,rechargeBO, rechargeFundBO);

        return PayStatusEnums.SUCCESS.getCode().equals(rechargeFundBO.getStatus());
    }

    /**
     * 手续费转账处理流程
     *
     * @param rechargeBO        充值单
     * @param rechargeFundBO    手续费资金单
     * @return                  流程成功与否
     */
    private boolean feeProcess(RechargeBO rechargeBO, RechargeFundBO rechargeFundBO){

        //调用账务转账接口
        AccountFundResBO accountFundResBO = accountRechargeService.feeFundHandle(rechargeBO, rechargeFundBO);

        AccountResultConverter.result2RechargeBO(accountFundResBO,rechargeBO, rechargeFundBO);

        return PayStatusEnums.SUCCESS.getCode().equals(rechargeFundBO.getStatus());
    }

    /**
     * 状态推进
     *
     * @param rechargeBO    充值单
     */
    private void orderStatusPush(RechargeBO rechargeBO){

        rechargeOrderRepository.update(rechargeBO);
    }

    /**
     * 线下充值订单重试推进（订单状态推进）
     *
     * <p>
     * 线下充值重试，即订单异步推进。步骤如下：
     * 1、验证原单状态(主单状态)，原单状态成功，订单无需再次推进。直接返回。
     * 2、找出账务充值单，若该明细单状态是INIT，则继续调用账务进行充值。
     * 3、找出充值手续费单，若存在收费资金单，则需要再次调用账务转账接口，收取线下打款手续费。
     *
     * 注意：目前线下充值订单信息，没有银行充值资金单，只包含账务充值转账单（只有一笔该单），
     * 以及可能存在的“线下充值打款业务” 手续费资金单（也只存在一笔该单）。
     * </p>
     *
     * @param rechargeBO     充值请求对象
     * @return ResponseBO    重试返回对象
     */
    public ResponseBO dalInnerFundProc(RechargeBO rechargeBO){

        log.info("线下充值订单重试推进, payNo:{}, status:{}", rechargeBO.getPayNo(), rechargeBO.getStatus());

        ResponseBO responseBO = new ResponseBO();

        //订单流程推进
        orderProcessPush(rechargeBO, responseBO);

        //订单状态推进
        orderStatusPush(rechargeBO);

        return responseBO;
    }

    /**
     * 订单流程推进
     *
     * @param rechargeBO    充值请求对象
     */
    private void orderProcessPush(RechargeBO rechargeBO, ResponseBO responseBO){

        //步骤1
        if(!PayStatusEnums.INIT.getCode().equals(rechargeBO.getStatus())) {
            responseBO.setStatus(rechargeBO.getStatus());
            responseBO.setRealFlag(true);
            return;
        }

        //充值资金单
        RechargeFundBO rechargeFundBO = rechargeBO.getRechargeFund(FundTypeEnum.RECHARGE.getCode());

        //步骤2
        if( PayStatusEnums.INIT.getCode().equals(rechargeBO.getStatus()) ) {

            if(!rechargeProcess(rechargeBO, rechargeFundBO)) {
                BO2BOConverter.getPayResponse(rechargeBO, responseBO);
                return ;
            }
        }

        //手续费资金单
        RechargeFundBO feeFundBO = rechargeBO.getRechargeFund(FundTypeEnum.FEE.getCode());

        //步骤3
        if( !Objects.isNull(feeFundBO) && PayStatusEnums.INIT.getCode().equals(feeFundBO.getStatus()) ){

            if(!feeProcess(rechargeBO, feeFundBO)) {
                BO2BOConverter.getPayResponse(rechargeBO, responseBO);
                return ;
            }
        }
        responseBO.setStatus(PayStatusEnums.SUCCESS.getCode());
        responseBO.setRealFlag(true);
    }

}
